/*-------------------------------------------------------------------------
   Copyright (C) 2010 One Laptop per Child

   This program is free software; you can redistribute it and/or modify it
   under the terms of the GNU General Public License as published by the
   Free Software Foundation; either version 2, or (at your option) any
   later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software
   Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.

   In other words, you are welcome to use, share and improve this program.
   You are forbidden to forbid anyone else to use, share and improve
   what you give them.   Help stamp out software-hoarding!

   As a special exception, you may use this file as part of a free software
   library for the XO of the One Laptop per Child project without restriction.
   Specifically, if other files instantiate
   templates or use macros or inline functions from this file, or you compile
   this file and link it with other files to produce an executable, this
   file does not by itself cause the resulting executable to be covered by
   the GNU General Public License.  This exception does not however
   invalidate any other reasons why the executable file might be covered by
   the GNU General Public License.
-------------------------------------------------------------------------*/

#include "cc_compat.h"
#include "stdio.h"
#include "ps2.h"
#include "queue.h"
#include "private/ps2_private.h"
#include "io.h"
#include "io3731.h"
#include "timer.h"
#include "sdi.h"
#include "keyboard.h"
#include "touchpad.h"

#define	KBCID_NOSTATE 			0
#define	KBCID_PWRUP 			1
#define	KBCID_WAIT_FOR_AA		2
#define KBCID_SEND_ID_CMD		3
#define KBCID_WAIT_ID_ACK		4
#define KBCID_WAIT_ID1			5
#define KBCID_WAIT_ID2			6
#define KBCID_CHECK_ID			7
#define KBCID_WAIT_MATRIX_ACK	8
#define KBCID_RETRY				9
#define KBCID_DRAIN				10
#define KBCID_ALPS_DRAIN		11
#define KBCID_SEND_DISABLE		12
#define KBCID_WAIT_DIS_ACK		13
#define KBCID_SEND_ENABLE		14
#define KBCID_WAIT_ENA_ACK		15
#define KBCID_FINAL_ENABLE		16
#define KBCID_PWRUP_DELAY		17
#define KBCID_ALPS_AA_DELAY		18
#define KBCID_ALPS_F5_DELAY		19

#define KBCID_DONE				0xfe

static unsigned char xdata kbc_id_state;
static unsigned char xdata kbc_id_retry;
static unsigned int	xdata kbc_id;
static int	xdata kbcid_timer;

unsigned char kbc_id_get_ack(void);

void kbc_id_start(void)
{
	kbc_id_state = KBCID_PWRUP;
}

void kbc_id_stop(void)
{
	kbc_id_state = KBCID_NOSTATE;
}

void run_kbc_id(void)
{
// #define DEBUG_KBC_STATE 1
#ifdef DEBUG_KBC_STATE
	{
		static unsigned char xdata last_kis = 0xff;
		if (kbc_id_state != last_kis)
		{
			print_tstamp();
			printf("kbc_id_state %d\n", (int) kbc_id_state);
			last_kis = kbc_id_state;
		}
	}
#endif

	switch (kbc_id_state)
	{
		case 0:
			break;

		case KBCID_PWRUP:
			kbc_id_retry=0;

			enable_EN_KBD_PWR();
			print_tstamp();
			puts("EN_KBD_PWR");

			ps2_enable();
			ps2_start();

			kbcid_timer = get_ms_timer();
			kbc_id_state = KBCID_PWRUP_DELAY;
			kbc_id = 0;
			break;

		case KBCID_PWRUP_DELAY:
			if (check_timer(kbcid_timer,100))
			{
				kbcid_timer = get_ms_timer();
				print_tstamp();
				puts("KBCID");
				kbc_id_state = KBCID_WAIT_FOR_AA;
			}
			break;

		case KBCID_WAIT_FOR_AA:

			if (ps2_kb_queue_size() > 0 )
			{
				if (ps2_kb_queue_get() == 0xaa)
				{
					print_tstamp();
					puts("Got 0xAA");
					kbc_id_state = KBCID_SEND_DISABLE;
				}
				else
				{
					print_tstamp();
					printf("PreAA 0x%x\n",(unsigned int)ps2_get_data());
				}
			}

			if (check_timer(kbcid_timer,20))
			{
				kbcid_timer = get_ms_timer();
				print_tstamp();
				puts("No BAT 0xAA");
				puts("Suspect ALPS");
				// If we don't have a BAT by now it may be an ALPS
				// device and we have to wait much longer before talking to
				// it.
				kbc_id_state = KBCID_ALPS_AA_DELAY;
			}
			break;

		case KBCID_ALPS_AA_DELAY:

			if (ps2_kb_queue_size() > 0 )
			{
				if (ps2_kb_queue_get() == 0xaa)
				{
					print_tstamp();
					puts("ALPS 0xAA");

					kbcid_timer = get_ms_timer();
					kbc_id_state = KBCID_SEND_DISABLE;
				}
				else
				{
					print_tstamp();
					printf("PreAA 0x%x int=0x%x\n",(unsigned int)ps2_get_data(),(unsigned int)ps2_get_int_flags());
				}
			}

			if (check_timer(kbcid_timer,300))
			{
				print_tstamp();
				puts("Still no 0xAA");
				puts("Continuing anyway");

				kbc_id_state = KBCID_SEND_DISABLE;
			}
			break;

			// ALPS still needs time before it will respond.
		case KBCID_ALPS_F5_DELAY:
			if (check_timer(kbcid_timer,100))
			{
				kbc_id_state = KBCID_SEND_DISABLE;
			}
			break;

			// Tell the keyboard to turn off its maxtrix.
		case KBCID_SEND_DISABLE:

			ps2_tx_queue_add(DEST_KEYBOARD,0xf5);
			ps2_enable_tx_int();
			ps2_kick_tx();
			kbcid_timer = get_ms_timer();
			kbc_id_state = KBCID_WAIT_DIS_ACK;
			print_tstamp();
			puts("f5->");
			break;

		case KBCID_WAIT_DIS_ACK:
			switch (kbc_id_get_ack())
			{
					// No data yet
				case 0:
					break;

					// Data ok move on.
				case 1:
					print_tstamp();
					printf("ack\n");
					kbc_id_state = KBCID_SEND_ID_CMD;
					break;
					// Error.
				default:
					kbc_id_state = KBCID_RETRY;
					break;
			}

			if (check_timer(kbcid_timer,100))
			{
				kbcid_timer = get_ms_timer();
				//Timeout
				print_tstamp();
				printf("f5 ack timeout\n");
				kbc_id_state = KBCID_RETRY;
				break;
			}
			break;

		case KBCID_SEND_ID_CMD:
			// send the id command
			ps2_tx_queue_add(DEST_KEYBOARD,0xf2);
			kbcid_timer = get_ms_timer();
			kbc_id_state = KBCID_WAIT_ID_ACK;
			print_tstamp();
			puts("f2->");
			break;

		case KBCID_WAIT_ID_ACK:

			switch (kbc_id_get_ack())
			{
					// No data yet
				case 0:
					break;

					// Ok on.
				case 1:
					print_tstamp();
					printf("ack\n");
					kbc_id_state = KBCID_WAIT_ID1;
					kbcid_timer = get_ms_timer();
					break;
					// Error.
				default:
					kbc_id_state = KBCID_RETRY;
					break;
			}
			if (check_timer(kbcid_timer,15))
			{
				kbcid_timer = get_ms_timer();
				//Timeout
				print_tstamp();
				printf("f2 ack timeout\n");
				kbc_id_state = KBCID_RETRY;
			}
			break;

			// wait for ID response of byte 1
		case KBCID_WAIT_ID1:
			if (ps2_kb_queue_size() > 0 )
			{
				// Read ID byte 1
				kbc_id = ps2_kb_queue_get();
				kbc_id <<= 8;
				kbcid_timer = get_ms_timer();
				kbc_id_state = KBCID_WAIT_ID2;
				break;
			}

			if (check_timer(kbcid_timer,15))
			{
				kbcid_timer = get_ms_timer();
				//Timeout
				print_tstamp();
				printf("ID1 timeout\n");
				kbc_id_state = KBCID_RETRY;
			}
			break;

			// wait for ID response of byte 2
		case KBCID_WAIT_ID2:

			if (ps2_kb_queue_size() > 0 )
			{
				// Read ID byte 2
				kbc_id |= ps2_kb_queue_get();
				kbcid_timer = get_ms_timer();
				kbc_id_state = KBCID_CHECK_ID;
				break;
			}

			if (check_timer(kbcid_timer,15))
			{
				kbcid_timer = get_ms_timer();
				//Timeout
				print_tstamp();
				printf("ID2 timeout\n");
				kbc_id_state = KBCID_RETRY;
			}
			break;

		case KBCID_CHECK_ID:
			switch (kbc_id)
			{
				case 0xab41:
					print_tstamp();
					puts("EnE KBC: 0xf7->");
					// send the 0xf7 command
					// 0xf7 tells the EnE controller to send matrix addresses rather than
					// set 2 codes.
					ps2_set_keyboard_target();
					ps2_clear_all_int_flags();
					ps2_send_data(0xf7);
					kbcid_timer = get_ms_timer();
					kbc_id_state = KBCID_WAIT_MATRIX_ACK;
					break;

				case 0xab83:
					print_tstamp();
					puts("ALPS KBC: Set2+");
					kbc_id_state = KBCID_SEND_ENABLE;
					break;

				default:
					print_tstamp();
					printf("Unknown kbc ID: 0x%x\n",kbc_id);
					kbc_id_state = KBCID_RETRY;
			}
			break;

		case KBCID_WAIT_MATRIX_ACK:

			switch (kbc_id_get_ack())
			{
					// No data yet
				case 0:
					break;

					// Ok on.
				case 1:
					print_tstamp();
					printf("ack\n");
					kbc_id_state = KBCID_SEND_ENABLE;
					break;
					// Error.
				default:
					kbc_id_state = KBCID_RETRY;
					break;
			}

			if (check_timer(kbcid_timer,15))
			{
				kbcid_timer = get_ms_timer();
				//Timeout
				print_tstamp();
				printf("0xf7 ACK timeout\n");
				kbc_id_state = KBCID_RETRY;
			}
			break;

		case KBCID_RETRY:
			print_tstamp();
			puts("kbcid retry");
			kbcid_timer = get_ms_timer();
			kbc_id_retry++;
			kbc_id_state = KBCID_DRAIN;

			if (kbc_id_retry > 3)
			{
				puts("ERROR: Unable to ID KBC");
				kbc_id_state = KBCID_FINAL_ENABLE;
			}
			break;

		case KBCID_DRAIN:

			if (ps2_kb_queue_size() > 0 )
			{
				print_tstamp();
				printf("D=0x%x\n",(unsigned int)ps2_kb_queue_get() );
				kbcid_timer = get_ms_timer();
			}
			// After no data for a bit. Retry.
			if (check_timer(kbcid_timer,15))
			{
				kbc_id_state = KBCID_SEND_DISABLE;
			}
			break;

			// Tell the keyboard to turn maxtrix on.
		case KBCID_SEND_ENABLE:
			ps2_tx_queue_add(DEST_KEYBOARD,0xf4);
			kbcid_timer = get_ms_timer();
			kbc_id_state = KBCID_WAIT_ENA_ACK;
			print_tstamp();
			puts("f4->");
			break;

		case KBCID_WAIT_ENA_ACK:

			switch (kbc_id_get_ack())
			{
					// No data yet
				case 0:
					break;

					// Ok move on.
				case 1:
					print_tstamp();
					printf("ack\n");
					kbc_id_state = KBCID_DONE;
					break;
					// Error.
				default:
					kbc_id_state = KBCID_RETRY;
					break;
			}

			if (check_timer(kbcid_timer,15))
			{
				kbcid_timer = get_ms_timer();
				//Timeout
				print_tstamp();
				printf("f4 ack timeout\n");
				kbc_id_state = KBCID_RETRY;
				break;
			}

			break;

			// In case of failure try one last time to re-enable the maxtrix.
		case KBCID_FINAL_ENABLE:
			ps2_tx_queue_add(DEST_KEYBOARD,0xf4);
			kbcid_timer = get_ms_timer();
			kbc_id_state = KBCID_DONE;
			print_tstamp();
			puts("f4->");
			break;

		case KBCID_DONE:
			key_start();
			print_tstamp();
			puts("KBCID done");
			kbc_id_state = 0;
			ps2_disable();
			ps2_tp_queue_clear();
			ps2_kb_queue_clear();
			ps2_tx_queue_clear();
			ps2_enable();
			// This is a hack for A3 boards. We used to have the touchpad on by default
			// and somewhere we stopped.  So go ahead and just turn it on
			// We don't have to care about the other board ID's because they won't have
			// a touchpad connected to the EC.
			touchpad_stream_on();

			break;
	}
}

unsigned char kbc_id_get_ack(void)
{
	unsigned char kbdat;

	if (ps2_kb_queue_size() > 0 )
	{
		kbdat = ps2_kb_queue_get();
		if (kbdat != 0xfa)
		{
			print_tstamp();
			printf("exp ACK got %x?\n",(unsigned int)kbdat);
			return 3;
		}
		// Data is an ACK. Sucess
		return 1;
	}

#if 0
	unsigned char flags;

	flags = ps2_get_int_flags();
	if ( flags & 0x0d )
	{
		// Error?
		if (flags & 0x0c)
		{
			print_tstamp();
			printf("ack err %x\n",flags);
			return 2;
		}
		else
		{
			// Got data.  What port?
			if (flags & 0x20)
			{
				// Data is from keyboard
				// Read the data
				if (ps2_get_data() != 0xfa)
				{
					print_tstamp();
					printf("exp ACK got %x?\n",(unsigned int)ps2_get_data());
					return 3;
				}

				// Data is 0xfa. Sucess
				return 1;
			}
			else
			{
				// Discard touch paddata
				printf("ick. TP data 0x%x\n",(unsigned int)ps2_get_data());
				return 4;
			}
		}
	}
#endif

	// No data.
	return 0;
}

